home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 3 / Info_Mac_1994-01.iso / Applications / SAT Quizzer / SATQuiz.c < prev    next >
C/C++ Source or Header  |  1993-07-09  |  12KB  |  492 lines

  1. /* Performs the following functions.
  2.     1) Allows the user to input data through a text file.
  3.     2) Reads in data from the SAT.dat file
  4.     3) "Randomizes" the data
  5.     4) Saves the data in randomized order
  6.     5) Gives the user a multiple choice quiz
  7.  
  8. SAT Quizzer 0.3 is written by Phil Sarin, and is a complete recoding of SAT
  9. Quizzer 0.22, also by Phil Sarin.
  10.  
  11.  
  12. I guess it's time for me to explain how this works.  The program uses three
  13. main methods of storing and accessing data.
  14.  
  15.     1)  The array of linked lists.  (allwords vocabulary)
  16.  
  17.         I use this structure in main(), initwords(), getdatafile(),
  18.         gettextfile(), and assemblelist().  The constant NUMSLOTS
  19.         determines the size of the array.  This structure might bring
  20.         the idea of chaining (in hash functions) to mind.  That is how
  21.         I thought it up.
  22.  
  23.         I wanted to be able to use the computer- generated random
  24.         numbers for a more randomized effect.  However, if I simply
  25.         picked a random number every time the program asked a
  26.         question, I would have problems..especially since every
  27.         question should be asked only once.  Imagine after
  28.         question 392 out of 393.  The program would have to keep
  29.         picking random numbers until it found the array index of the
  30.         one word which had not been asked.
  31.  
  32.         So, I thought up the chaining method.  Now, there are NUMSLOTS
  33.         (say 10 for example) array indices which each have a linked
  34.         list.  When a random number is generated, I just take an item
  35.         off of chain 0 - 9, depending on the number.  This way, when
  36.         word 392 out of 393 has been asked, the computer only needs to
  37.         generate numbers until the right number out of 10 is chosen.
  38.         The advantage:  it's a lot quicker to find 1/10 than 1/393.
  39.  
  40.  
  41.     2)  The singly linked list.  assemblelist(), putdatafile(), and quiz().
  42.         (wordlist listofwords)
  43.  
  44.         Before the program even starts asking questions, it takes the
  45.         array of linked lists and starts generating random numbers
  46.         between 0 and 9.  Each time, one item is removed from a
  47.         corresponding chain, and placed on a singly linked list.  This
  48.         list is written back to disk.  Thus, it is written back in a
  49.         different order than it was read in.
  50.  
  51.         The singly linked lists is a very programmer-friendly and easy
  52.         structure to use.  It was very easy to insert items on it in
  53.         assemblelist().  Additionally, both putdatafile(), and quiz()
  54.         simply went word to word down the entire list.  The entire
  55.         thing is dynamic, so the limitations are purely hardware
  56.         dependent.  I used this structure because of the ease of
  57.         insertion, and the ease of scrolling through the data.
  58.  
  59.     3)  The dynamic array.  quiz(), and showchoices() (ptrarray nodeptrs)
  60.  
  61.         This structure helped me overcome the limitations with earlier
  62.         versions of SAT Quizzer.  In order to generate multiple choice
  63.         questions with any tolerable amount of speed, I needed to be
  64.         able to somehow index into my data.  Indexing is virtually
  65.         impossible with a linked list.  In the earlier Pascal versions
  66.         of SAT Quizzer, I had used an array of pointers to overcome
  67.         this problem.  In Pascal, I had to set a maximum limit to the
  68.         number of words allowed in an array.  In C, using calloc(), I
  69.         overcame this problem.  This dynamic array would also be an
  70.         array of pointers.  Each slot in the dynamic array would point
  71.         to a corresponding node in the singly linked list.  This way,
  72.         in showchoices(), I could generate a random number between
  73.         1 and numwords, and easily index into it using the dynamic
  74.         array.
  75.  
  76.  
  77.     I can't possibly go over every nuance of every function.  The
  78.     explanation of the data structures should clarify most questions one
  79.     might have about the program.
  80. */
  81.  
  82. #include <stdio.h>
  83. #include <stdlib.h>
  84. #include <string.h>
  85. #include <ctype.h>
  86. #include <time.h>
  87. #include "defines.h"
  88. #include "typedefs.h"
  89. #include "prototypes.h"
  90.  
  91. int currentslot = 0; /* For getdatafile and gettextfile. */
  92. int numwords = 0;
  93.  
  94. void main(void)
  95. {
  96.  
  97.     allwords vocabulary;
  98.     wordlist listofwords;
  99.  
  100.     initwords(vocabulary);
  101.     initrandom();
  102.  
  103.     printf("Would you like to use the words which are already in SAT");
  104.     printf("Quizzer\'s database? ");
  105.     if(toupper(getchln()) != 'N')
  106.         getdatafile(vocabulary);
  107.  
  108.     printf("Would you like to input words through a text file? ");
  109.     if(toupper(getchln()) != 'N')
  110.         gettextfile(vocabulary);
  111.  
  112.  
  113.     listofwords = assemblelist(vocabulary);
  114.     putdatafile(listofwords);
  115.     quiz(listofwords);
  116.  
  117. }
  118.  
  119. char getchln(void)
  120. /* Returns a char read from the keyboard and flushes an EOLN if the char is
  121.    not an EOLN. */
  122. {
  123.  
  124.     char typedin, returnval;
  125.  
  126.     typedin = returnval = getchar();
  127.     while(typedin != '\n')
  128.         typedin = getchar();
  129.  
  130.     return returnval;
  131.  
  132. }
  133.  
  134. void initwords(allwords newwords)
  135. /* Initializes every slot in the newwords array to NULL. */
  136.  
  137. {
  138.  
  139.     int i;
  140.  
  141.     for(i = 0; i < NUMSLOTS; i++)
  142.         newwords[i] = NULL;
  143.  
  144. }
  145.  
  146. void initrandom(void)
  147. /* Initializes the rand() function with a clock seed. */
  148. {
  149.  
  150.     int utime;
  151.     long ltime;
  152.  
  153.  
  154.     ltime = time(NULL);
  155.     utime = (unsigned int) ltime / 2;
  156.  
  157.     srand(utime);
  158.  
  159. }
  160.  
  161. void getdatafile(allwords vocabulary)
  162. /* Reads in vocabulary from a binary data file. */
  163.  
  164. {
  165.  
  166.     FILE *infile;
  167.     wordlist currentword;
  168.     struct entry wordfromfile; /* Used for debugging.  I'll leave it in. */
  169.  
  170.     putchar('\n');
  171.     printf("Opening data file...\n");
  172.  
  173.     if((infile = fopen(DATAPATH, "rb")) == NULL)
  174.         printf("Cannot open file.  A new file will be made.\n");
  175.     else  {
  176.         printf("Reading...\n");
  177.         while(!feof(infile))  {
  178.  
  179.             if(fread(&wordfromfile, sizeof wordfromfile, 1,infile)
  180.               != 1)
  181.                 break; /* Don't know why, but EOF is
  182.                       unexpectedly encountered here.
  183.                       That's why there's a break, and not
  184.                       an exit. */
  185.  
  186.             currentword = wordalloc();
  187.             currentword->info = wordfromfile;
  188.             currentword->next = vocabulary[currentslot];
  189.             vocabulary[currentslot] = currentword;
  190.             currentslot = (currentslot + 1) % NUMSLOTS;
  191.         }
  192.  
  193.     }
  194.  
  195.     printf("Closing data file...\n\n");
  196.     fclose(infile);
  197.  
  198. }
  199.  
  200. void gettextfile(allwords vocabulary)
  201. /* Reads in vocabulary from a text file created as such:
  202.  
  203. partofspeech,word,definition
  204.  
  205. i.e.
  206.  
  207. adj,loud,noisy
  208.  
  209. */
  210. {
  211.  
  212.     FILE *textfile;
  213.     char textpath[20], inchar;
  214.     wordlist currentword;
  215.     int currentletter;
  216.  
  217.     printf("Please type in the name of the text file. ");
  218.     gets(textpath);
  219.  
  220.     printf("\nOpening text file...\n");
  221.  
  222.     if((textfile = fopen(textpath, "r")) == NULL)
  223.         printf("Could not open file.\n");
  224.     else  {
  225.         printf("Reading...\n");
  226.         while(!feof(textfile)) {
  227.  
  228.             currentword = wordalloc();
  229.             /**** Find part of speech. ****/
  230.  
  231.             fgetc(textfile);
  232.             fgetc(textfile);
  233.  
  234.             /* The third letters are all distinct. */
  235.  
  236.             switch(toupper(fgetc(textfile)))  {
  237.                 case 'U' :
  238.                     currentword->info.partofspeech = noun;
  239.                     break;
  240.                 case 'R' :
  241.                     currentword->info.partofspeech = verb;
  242.                     break;
  243.                 case 'J' :
  244.                     currentword->info.partofspeech = adj;
  245.                     break;
  246.                 default :
  247.                     currentword->info.partofspeech = other;
  248.             }
  249.  
  250.             /* Flush out until comma. */
  251.  
  252.             while(fgetc(textfile) != ',')  ;
  253.  
  254.             /**** Get word. ****/
  255.  
  256.             for(inchar = fgetc(textfile), currentletter = 0;
  257.                 inchar != ',' && currentletter < MAXWORD;
  258.                 inchar = fgetc(textfile), currentletter++)
  259.                 currentword->info.word[currentletter] =
  260.                     inchar;
  261.             /* The loop inputs char by char into the word until
  262.                a comma is encountered. */
  263.  
  264.             currentword->info.word[currentletter] = '\0';
  265.  
  266.             /**** Get meaning. ****/
  267.  
  268.             fgets(currentword->info.definition, MAXMEANING,
  269.                 textfile);
  270.             currentletter = strlen(currentword->info.definition);
  271.  
  272.             /* Check to see if letters were truncated because the
  273.                string in the data file was too long.  Flush to
  274.                a new line if they were.  If they weren't, remove
  275.                the '\n' from the string and replace with '\0'. */
  276.  
  277.             if(currentword->info.definition[currentletter-1]=='\n')
  278.                 currentword->info.definition[currentletter - 1]
  279.                      = '\0';
  280.             else
  281.                 while(fgetc(textfile) != '\n' &&
  282.                       !feof(textfile))
  283.                     ;
  284.  
  285.             /**** Attach word to data structure. ****/
  286.  
  287.             currentword->next = vocabulary[currentslot];
  288.             vocabulary[currentslot] = currentword;
  289.  
  290.             currentslot = (currentslot + 1) % NUMSLOTS;
  291.  
  292.         }
  293.  
  294.     }
  295.  
  296.     printf("Closing text file...\n\n");
  297.  
  298.     fclose(textfile);
  299.  
  300. }
  301.  
  302. wordlist wordalloc()
  303. /* Allocates enough space for a data object of type struct node. */
  304.  
  305. {
  306.  
  307.     return (wordlist) malloc(sizeof(struct node));
  308.  
  309. }
  310.  
  311. wordlist assemblelist(allwords vocabulary)
  312. /* Returns the pointer to a linked list. */
  313.  
  314. {
  315.  
  316.     wordlist nextnode, listsofar = NULL;
  317.     short done, slottouse;
  318.  
  319.  
  320.     printf("\nRandomizing...\n");
  321.  
  322.     for(slottouse = 0, done = 1; slottouse < NUMSLOTS && done; slottouse++)
  323.         done = vocabulary[slottouse] == NULL;
  324.  
  325.     while(!done)  {
  326.  
  327.         for(slottouse = rand() % NUMSLOTS; vocabulary[slottouse] ==
  328.             NULL; slottouse = rand() % NUMSLOTS)  ;
  329.         numwords++;
  330.         nextnode = vocabulary[slottouse];
  331.         vocabulary[slottouse] = vocabulary[slottouse]->next;
  332.         nextnode->next = listsofar;
  333.         listsofar = nextnode;
  334.         for(slottouse = 0, done = 1; slottouse < NUMSLOTS && done;
  335.             slottouse++)
  336.             done = vocabulary[slottouse] == NULL;
  337.  
  338.     }
  339.  
  340.     return listsofar;
  341.  
  342. }
  343.  
  344. void putdatafile(wordlist listofwords)
  345. /* Saves the data file to disk. */
  346.  
  347. {
  348.  
  349.     FILE *outfile;
  350.  
  351.     printf("\nOpening file...\n");
  352.     if((outfile = fopen(DATAPATH, "wb")) == NULL)  {
  353.         printf("Error opening file.\n");
  354.         exit(1);
  355.     }
  356.  
  357.     printf("Writing...\n");
  358.     while(listofwords)  {
  359.  
  360.         if(fwrite(&(listofwords->info), sizeof listofwords->info, 1,
  361.            outfile) != 1)  {
  362.             printf("Error writing to disk.\n");
  363.             exit(1);
  364.  
  365.         }
  366.  
  367.         listofwords = listofwords->next;
  368.  
  369.     }
  370.  
  371.     printf("Closing file...\n\n");
  372.     fclose(outfile);
  373.  
  374. }
  375.  
  376. void quiz(wordlist listofwords)
  377. /* Generates multiple choice questions. */
  378.  
  379. {
  380.  
  381.     char rightanswer, answer;
  382.     ptrarray nodeptrs, p;
  383.     wordlist q;
  384.     int right = 0, numdone = 0;
  385.  
  386.     nodeptrs = calloc(numwords, sizeof(wordlist));
  387.     for(p = nodeptrs, q = listofwords; q; p++, q = q->next)
  388.         *p = q;
  389.  
  390.     while(listofwords)  {
  391.         printpos(listofwords->info.partofspeech);
  392.         printf(".  %s\n", listofwords->info.word);
  393.         putchar('\n');
  394.         rightanswer = showchoices(nodeptrs,
  395.             listofwords->info.definition, listofwords->
  396.             info.partofspeech);
  397.         printf("\nAnswer:  ('a' - 'd', 'q' to quit) ");
  398.  
  399.         if((answer = tolower(getchln())) == 'q')
  400.             break;
  401.  
  402.         numdone++;
  403.  
  404.         if(answer == rightanswer)  {
  405.             printf("Right!\n");
  406.             ++right; 
  407.         }
  408.         else
  409.             printf("Sorry, the answer is %c.\n", rightanswer);
  410.         putchar('\n');
  411.         listofwords = listofwords->next;
  412.  
  413.     }
  414.  
  415.     printf("Thanks for using SAT Quizzer.  You got %d out of %d right.\n",
  416.            right, numdone);
  417.  
  418. }
  419.  
  420. void printpos(postype postoprint)
  421. {
  422.  
  423.     switch(postoprint)  {
  424.         case noun :
  425.             printf("noun");
  426.             break;
  427.         case verb :
  428.             printf("verb");
  429.             break;
  430.         case adj :
  431.             printf("adj");
  432.             break;
  433.         case other :
  434.             printf("adv. or prep");
  435.             break;
  436.     }
  437.  
  438. }
  439.  
  440. char showchoices(ptrarray nodeptrs, meaningtype correctmeaning, postype
  441.          correctpos)
  442.  
  443. /* Prints out all the multiple choice options which the user will have. This
  444. procedure is not very intuitively named unfortunately.  It returns the letter
  445. of the correct answer. */
  446.  
  447.  
  448. {
  449.  
  450.     short ok, i, j, answernum, randindex;
  451.     meaningtype options[NUMOPTIONS], testmeaning;
  452.     ptrarray p;
  453.     postype testpos;
  454.  
  455.     for(i = 0; i < NUMOPTIONS; i++)
  456.         options[i][0] = '\0';
  457.  
  458.     answernum = rand() % 4;
  459.     strcpy(options[answernum], correctmeaning);
  460.  
  461.     /* Fill in "dummy" choices and print them out. */
  462.  
  463.     for(i = 0, ok = 0; i < NUMOPTIONS; i++, ok = 0)  {
  464.         if(i != answernum)  {
  465.  
  466.             while(!ok)  {
  467.  
  468.                 randindex = rand() % numwords;
  469.                 p = nodeptrs + randindex;
  470.                 strcpy(testmeaning, (*p)->info.definition);
  471.                 testpos = (*p)->info.partofspeech;
  472.  
  473.                 for(j = 0, ok = testpos == correctpos; j < i &&
  474.                     ok; j++)
  475.                     ok = strcmp(testmeaning, options[j]) &&
  476.                         testpos == correctpos;
  477.  
  478.                 ok = ok && strcmp(testmeaning,
  479.                     options[answernum]);
  480.  
  481.             }
  482.             strcpy(options[i], testmeaning);
  483.         }
  484.  
  485.         printf("\t%c. %s\n", (char) (i + 'a'), options[i]);
  486.  
  487.     }
  488.  
  489.     return (char) (answernum + 'a');
  490.  
  491. }
  492.